#include "lexico.h"


Lexico::Lexico(string file_dir)
{
    file.open(file_dir, ios::in);
        this->linea=0;
        this->columna=0;
        this->token = new Token();
}

Token *Lexico::getNextToken()
{
    if(this->file.is_open()){
            int estado = 0;
            string lexema ="";
            int tokenid =0;
            char nextcaracter = -1;
            while(this->file.good()){

                char caracter = this->file.peek() ;
                    switch(estado){
                case 0:{

                    if(caracter == -1){
                        lexema ="$";
                        //token = new Token(tokentype.fin_archivo,lexema,this->linea,this->columna);
                        this->token->setCod(tokentype.fin_archivo);
                        this->token->setLexema(lexema);
                        this->token->setLinea(this->linea);
                        this->token->setColumna(this->columna);

                        return this->token;
                    }else{
                            if(isspace(caracter)){
                                estado =1;
                            }else{
                                if(caracter == '+' ||caracter == '-' ||caracter == '(' ||caracter == ')' ||
                                caracter == '^' ||caracter == '[' ||caracter == ']' ||caracter == ','||
                                caracter == ':'||caracter == '.'||caracter == '=' || caracter == '*'|| caracter == '{'|| caracter == '}')
                                {
                                    estado = 2;
                                }else{
                                    if(caracter == '<' || caracter ==  '>' ){
                                    estado=3;
                                    }else{
                                        if(caracter == '/'){
                                        estado = 4;
                                        }else{
                                            if(isalpha(caracter)|| caracter=='_'){
                                            estado = 5;
                                            }else{
                                                if(isdigit(caracter)){
                                                estado = 6;
                                                }else{
                                                    if(caracter == '\''){
                                                    estado = 7;
                                                    }else{
                                                        if(caracter == '"'){
                                                        estado = 8;
                                                        }
                                                    }

                                                }

                                            }

                                        }


                                    }

                                }



                            }

                    }


                    if(estado==0){

                        stringstream out;
                        out << "Error Lexico: Caracter No Valido" << endl;
                        out << "Fila:  ";
                        out << this->linea;
                        out << " Columna: ";
                        out << this->columna<< endl;
                        throw  exception(out.str().c_str());
                    }


                    break;}
                case 1:{
                    this->file.get();
                    if(caracter =='\n'|| caracter == '\r' || caracter == '\r\n'){
                        lexema = caracter;
                        //token = new Token(tokentype.enter,"Fin de Linea",this->linea,this->columna);
                        this->token->setCod(tokentype.enter);
                        this->token->setLexema("Fin de Linea");
                        this->token->setLinea(this->linea);
                        this->token->setColumna(this->columna);
                    this->columna =0;
                    this->linea++;

                    return this->token;
                    }else{
                    this->columna++;
                    }
                    estado = 0;
                    break;}
                case 2:{
                    caracter = this->file.get();
                    lexema = caracter;
                    this->columna++;
                    tokenid =0;
                    if(caracter == '+'){
                        tokenid = tokentype.oprSuma;
                    }
                    if(caracter == '-'){
                        tokenid = tokentype.oprResta;
                    }
                    if(caracter == '('){
                        tokenid = tokentype.pntparIzq;
                    }
                    if(caracter == ')'){
                        tokenid = tokentype.pntparDer;
                    }
                    if(caracter == '^'){
                        tokenid = tokentype.oprExpo;
                    }
                    if(caracter == '['){
                        tokenid = tokentype.pntcorIzq;
                    }
                    if(caracter == ']'){
                        tokenid = tokentype.pntcorDer;
                    }
                    if(caracter == ','){
                        tokenid = tokentype.pntComa;
                    }
                    if(caracter == ':'){
                        tokenid = tokentype.pntDosPuntos;
                    }
                    if(caracter == '.'){
                        tokenid = tokentype.pntPunto;
                    }
                    if(caracter == '='){
                        tokenid = tokentype.oprIgual;
                    }
                    if(caracter == '*'){
                        tokenid = tokentype.oprMult;
                    }
                    if(caracter == '{'){
                        tokenid = tokentype.pntkeyIzq;
                    }
                    if(caracter == '}'){
                        tokenid = tokentype.pntkeyDer;
                    }
                    //token = new Token(tokenid,lexema,this->linea,this->columna);
                        this->token->setCod(tokenid);
                        this->token->setLexema(lexema);
                        this->token->setLinea(this->linea);
                        this->token->setColumna(this->columna);
                    return this->token;
                    break;}
                case 3:{
                    caracter = this->file.get();
                    lexema = caracter;
                    this->columna++;
                    tokenid =0;
                    nextcaracter = this->file.peek();
                    switch(caracter){
                    case '<':
                        switch(nextcaracter){
                        case '=':
                            caracter = this->file.get();
                            this->columna++;
                            tokenid = tokentype.oprMenorIgual;
                            lexema.append(1,caracter);
                        break;
                        case '-':
                            caracter = this->file.get();
                            this->columna++;
                            tokenid = tokentype.oprAsig;
                            lexema.append(1,caracter);
                            break;
                        case '>':
                            caracter = this->file.get();
                            this->columna++;
                            tokenid = tokentype.oprDist;
                            lexema.append(1,caracter);
                            break;

                        default:
                            tokenid = tokentype.oprMenor;
                            break;

                        }
                    break;
                    case '>':
                        switch(nextcaracter){
                        case '=':
                            caracter = this->file.get();
                            this->columna++;
                            tokenid = tokentype.oprMayorIgual;
                            lexema.append(1,caracter);
                        break;

                        default:
                            tokenid = tokentype.oprMayor;
                            break;

                        }

                        break;


                    }
                    //token = new Token(tokenid,lexema,this->linea,this->columna);
                    this->token->setCod(tokenid);
                        this->token->setLexema(lexema);
                        this->token->setLinea(this->linea);
                        this->token->setColumna(this->columna);
                    return this->token;
                    break;}
                case 4:{
                    caracter = this->file.get();
                    lexema = caracter;
                    char c =-1;
                    switch(this->file.peek()){
                        case '/':

                            while((c=this->file.peek()) != '\n' && (c=this->file.peek()) != '\r'&& (c=this->file.peek())!= '\r\n' ){
                                c = this->file.get();
                            }
                            this->columna=0;
                            //this->linea++;
                            estado = 0;

                        break;
                        case '*':{
                            int commentL = this->linea;
                            int commentC = this->columna;
                            string fincomment = "";
                            c = this->file.get();
                            this->columna++;
                            c = this->file.get();
                            this->columna++;

                            fincomment.append(1,c);
                            fincomment.append(1,this->file.peek());

                            while(fincomment != "*/" && c != -1 ){

                                if(c =='\n'|| c == '\r' || c == '\r\n' ){
                                this->columna =0;
                                this->linea++;
                                }else{
                                    this->columna++;
                                }
                                c = this->file.get();
                                fincomment = "";
                                fincomment.append(1,c);
                                fincomment.append(1,this->file.peek());

                            }

                            if(c == -1){
                                stringstream out;
                                out<< "Error Lexico: se esperaba fin de Comentario */"<< endl;
                                out<< "Fila:  ";
                                out<< commentL;
                                out<< " Columna: ";
                                out<< commentC << endl;
                                throw  exception(out.str().c_str());
                            }else{
                                this->file.get();
                                estado = 0;
                            }
                            break;}
                        default:
                            this->token->setCod(tokentype.oprDiv);
                        this->token->setLexema(lexema);
                        this->token->setLinea(this->linea);
                        this->token->setColumna(this->columna);
                        return this->token;
                            //return token = new Token(tokentype.oprDiv,lexema,this->linea,this->columna);
                            break;
                    }


                    break;}
                case 5:{
                    lexema.append(1,this->file.get());
                    this->columna++;
                    char c = -1;
                    if(isalnum(c=this->file.peek())||(c=this->file.peek())=='_'){
                        estado = 5;
                    }else{
                        tokentype.it = tokentype.find(lexema);
                    if(tokentype.it != tokentype.reservadas.end()){
                        //token = new Token(tokentype.it->second,lexema,this->linea,this->columna);
                        this->token->setCod(tokentype.it->second);
                        this->token->setLexema(lexema);
                        this->token->setLinea(this->linea);
                        this->token->setColumna(this->columna);
                        return this->token;
                    }else{
                        //token = new Token(tokentype.litIdentificador,lexema,this->linea,this->columna);
                        this->token->setCod(tokentype.litIdentificador);
                        this->token->setLexema(lexema);
                        this->token->setLinea(this->linea);
                        this->token->setColumna(this->columna);
                        return this->token;
                    }
                    }



                    break;}
                case 6:{
                    lexema = this->file.get();
                    this->columna++;
                    char d =-1;
                    while(isdigit((d=this->file.peek()))){
                        lexema.append(1,d);
                        this->columna++;
                        this->file.get();
                    }
                    if(!isspace(d) && d!=')'&& d!= '('&& d!= '[' && d!= ']' && d!= ',' && d!= '+' && d!= '-' && d!= '*' && d!= '/' && d!= '<' && d!= '>' && d!= '^' && d!= ':' && d!= '='&& d!= '{' && d!= '}'  && d!= -1){
                        if(d== '.'){
                            lexema.append(1,d);
                            this->file.get();
                            if(!isspace(this->file.peek())){
                                while(isdigit((d=this->file.peek()))){
                                    lexema.append(1,d);
                                    this->columna++;
                                    this->file.get();
                                }
                                if(!isspace(d) && d!=')'&& d!= '('&& d!= '[' && d!= ']' && d!= ',' && d!= '+' && d!= '-' && d!= '*' && d!= '/' && d!= '<' && d!= '>' && d!= '^' && d!= ':' && d!= '='&& d!= '{' && d!= '}' && d!= -1){
                                    stringstream out;
                                    out<<"Error Lexico: Se esperaba digitos no caracteres"<<endl;
                                    out << "Fila:  ";
                                    out << this->linea;
                                    out << " Columna: ";
                                    out << this->columna<< endl;
                                    throw exception(out.str().c_str());
                                }else{
                                    //token = new Token(tokentype.litNumeroReal,lexema,this->linea,this->columna);
                                    this->token->setCod(tokentype.litNumeroReal);
                                    this->token->setLexema(lexema);
                                    this->token->setLinea(this->linea);
                                    this->token->setColumna(this->columna);
                                    return this->token;
                                }
                            }
                        }else{

                            stringstream out;
                            out<<"Error Lexico: Se esperaba un . o mas digitos"<<endl;
                            out << "Fila:  ";
                            out << this->linea;
                            out << " Columna: ";
                            out << this->columna<< endl;
                            throw exception(out.str().c_str());

                        }
                    }

                    lexema = lexema.substr(0,lexema.size());
                    //token = new Token(tokentype.litNumeroEntero,lexema,this->linea,this->columna);
                    this->token->setCod(tokentype.litNumeroEntero);
                                    this->token->setLexema(lexema);
                                    this->token->setLinea(this->linea);
                                    this->token->setColumna(this->columna);
                                    return this->token;

                    break;}
                case 7:{
                    lexema = this->file.get();
                    this->columna++;
                    if(this->file.peek()!='\''){
                    lexema.append(1,this->file.get());
                    this->columna++;
                    }else{
                        stringstream out;
                                    out<<"Error Lexico: No Pueden ir Dos Commilas Simples juntas"<<endl;
                                    out << "Fila:  ";
                                    out << this->linea;
                                    out << " Columna: ";
                                    out << this->columna<< endl;
                                    throw  exception(out.str().c_str());
                    }
                    if(this->file.peek() == '\''){
                        this->columna++;
                        lexema.append(1,this->file.get());
                        //token = new Token(tokentype.litCaracter,lexema,this->linea,this->columna);
                        this->token->setCod(tokentype.litCaracter);
                        this->token->setLexema(lexema);
                        this->token->setLinea(this->linea);
                        this->token->setColumna(this->columna);

                        return this->token;
                    }else{

                                    stringstream out;
                                    out<<"Error Lexico: Se esperaba comilla simple"<<endl;
                                    out << "Fila:  ";
                                    out << this->linea;
                                    out << " Columna: ";
                                    out << this->columna<< endl;
                                    throw  exception(out.str().c_str());
                    }
                    break;}
                case 8:{
                    lexema = this->file.get();
                    this->columna++;
                    while(this->file.peek() != '"' && this->file.peek() != '\n' && this->file.peek() != '\r' && this->file.peek() != '\r\n' && this->file.peek() != -1){

                        lexema.append(1,this->file.get());
                        this->columna++;

                    }
                    if(this->file.peek()=='"'){
                        lexema.append(1,this->file.get());
                        this->columna++;
                        //token = new Token(tokentype.litCadena,lexema,this->linea,this->columna);

                        this->token->setCod(tokentype.litCadena);
                        this->token->setLexema(lexema);
                        this->token->setLinea(this->linea);
                        this->token->setColumna(this->columna);

                        return this->token;
                    }else{

                            stringstream out;
                            out<<"Error Lexico: Se esperaba comilla doble"<<endl;
                            out << "Fila:  ";
                            out << this->linea;
                            out << " Columna: ";
                            out << this->columna<< endl;
                            throw  exception(out.str().c_str());


                    }
                    break;}

                }



            }

            lexema ="$";
            //token = new Token(tokentype.fin_archivo,lexema,this->linea,this->columna);

            this->token->setCod(tokentype.fin_archivo);
                        this->token->setLexema(lexema);
                        this->token->setLinea(this->linea);
                        this->token->setColumna(this->columna);
            return this->token;


        }else{
            stringstream out;
            out<<"Error Lexico: No se Pudo Abrir El Archivo"<<endl;
            throw exception(out.str().c_str());
        }
}

void Lexico::fileClose()
{
    this->file.close();
}
